﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.ComponentModel;
using System.Windows.Forms;

namespace Client.p2p
{
    public class PrivateChatCmdBackground : CommonCmdBackground
    {
        private TcpClient tcpClient;
        public TcpClient TcpClient
        {
            get { return tcpClient; }
        }
        NetworkStream networkStream;
        BackgroundWorker bwReceiver;
        int listenPort = int.Parse(global::Client.Properties.Resources.ListenPort);
        string LocalName;
        string TargetName;

        #region Constructors and Deconstructors

        /// <summary>
        /// Constructor 1
        /// </summary>
        /// <param name="TargetIP">TargetIP</param>
        /// <param name="LocalName">LocalName</param>
        /// <param name="TargetName">TargetName</param>
        public PrivateChatCmdBackground(IPAddress TargetIP, string LocalName, string TargetName)
        {
            this.LocalName = LocalName;
            this.TargetName = TargetName;
            BackgroundWorker bwConnector = new BackgroundWorker();
            bwConnector.DoWork += new DoWorkEventHandler(bwConnector_DoWork);
            bwConnector.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwConnector_RunWorkerCompleted);
            bwConnector.RunWorkerAsync(TargetIP);
        }

        /// <summary>
        /// Constructor 2
        /// </summary>
        /// <param name="tcpClient">tcpClient</param>
        /// <param name="LocalName">LocalName</param>
        /// <param name="TargetName">TargetName</param>
        public PrivateChatCmdBackground(TcpClient tcpClient, string LocalName, string TargetName)
        {
            this.tcpClient = tcpClient;
            this.networkStream = tcpClient.GetStream();
            this.LocalName = LocalName;
            this.TargetName = TargetName;
            this.bwReceiver = new BackgroundWorker();
            this.bwReceiver.WorkerSupportsCancellation = true;
            this.bwReceiver.DoWork += new DoWorkEventHandler(bwReceiver_DoWork);
            this.bwReceiver.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwReceiver_RunWorkerCompleted);
            this.bwReceiver.RunWorkerAsync();
            this.semaphore.Release();
        }

        ~PrivateChatCmdBackground()
        {
            if (this.bwReceiver.IsBusy)
            {
	            this.bwReceiver.CancelAsync();
            }
        }

        #endregion

        #region Connect Work

        private void bwConnector_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
	            IPAddress targetIP = (IPAddress)e.Argument;
	            this.tcpClient = new TcpClient();
                this.tcpClient.Connect(targetIP, listenPort);
                this.networkStream = this.tcpClient.GetStream();
	            this.bwReceiver = new BackgroundWorker();
	            this.bwReceiver.WorkerSupportsCancellation = true;
	            this.bwReceiver.DoWork += new DoWorkEventHandler(bwReceiver_DoWork);
	            this.bwReceiver.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwReceiver_RunWorkerCompleted);
	            this.bwReceiver.RunWorkerAsync();
                e.Result = true;
            }
            catch (System.Exception ex)
            {
                System.Diagnostics.Debug.Print(ex.Message);
                e.Result = false;
            }
        }

        private void bwConnector_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if ((bool)e.Result)
            {
//                 OnConnectingSuccessed(new EventArgs());
                this.semaphore.Release();
            }
            else
            {
//                 OnConnectingFailed(new EventArgs());
            }
            ((BackgroundWorker)sender).Dispose();
            GC.Collect();
        }

        #endregion

        #region Receive Work

        private void bwReceiver_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                BackgroundWorker bw = sender as BackgroundWorker;
	            while (!bw.CancellationPending && this.tcpClient.Connected)
	            {
	                //Do work!!!
                    byte[] buffer = new byte[4];
                    int readBytes = networkStream.Read(buffer, 0, 4);
                    if (readBytes == 0) break;
                    CommandType cmdType = (CommandType)(BitConverter.ToInt32(buffer, 0));
                    buffer = new byte[4];
                    readBytes = networkStream.Read(buffer, 0, 4);
                    if (readBytes == 0) break;
                    int senderNameSize = BitConverter.ToInt32(buffer, 0);
                    buffer = new byte[senderNameSize];
                    readBytes = networkStream.Read(buffer, 0, senderNameSize);
                    if (readBytes == 0) break;
                    string senderName = Encoding.Unicode.GetString(buffer);
                    buffer = new byte[4];
                    readBytes = networkStream.Read(buffer, 0, 4);
                    if (readBytes == 0) break;
                    int metaDataSize = BitConverter.ToInt32(buffer, 0);
                    if (metaDataSize > 0)
                    {
                        buffer = new byte[metaDataSize];
                        readBytes = networkStream.Read(buffer, 0, metaDataSize);
                    }
                    else buffer = null;
                    if (readBytes == 0) break;
                    //string metaData = Encoding.ASCII.GetString(buffer);
                    if (senderName != this.TargetName) continue;
//                     OnCommandReceived(new CommandEventArgs(new Command(cmdType,
//                         ((IPEndPoint)tcpClient.Client.LocalEndPoint).Address,
//                         ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address,
//                         senderName,
//                         buffer)));
                }
                e.Result = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                e.Result = false;
            }
            tcpClient.Close();
        }

        private void bwReceiver_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if ((bool)e.Result)
            {
//                 OnDisconnectedFromRemote(new EventArgs());
            }
            else
            {
                OnRemoteDisconnected(new RemoteEventArgs(tcpClient.Client));
            }
            ((BackgroundWorker)sender).Dispose();
            GC.Collect();
        }

        #endregion

        #region Send Work

        private System.Threading.Semaphore semaphore = new System.Threading.Semaphore(0, 1);
        void bwSender_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
	            semaphore.WaitOne();
	            Command cmd = (Command)e.Argument;
	            //Command Type
	            byte[] buffer = BitConverter.GetBytes((int)cmd.CommandType);
	            this.networkStream.Write(buffer, 0, 4);
	            //SenderName
	            buffer = BitConverter.GetBytes(Encoding.Unicode.GetByteCount(LocalName));
	            this.networkStream.Write(buffer, 0, 4);
	            buffer = Encoding.Unicode.GetBytes(LocalName);
	            this.networkStream.Write(buffer, 0, buffer.Length);
	            //MetaData
                if (cmd.MetaData == null)
                {
                    networkStream.Write(new byte[4] { 0, 0, 0, 0 }, 0, 4);
                }
                else
                {
                    buffer = BitConverter.GetBytes(cmd.MetaData.Length);
                    networkStream.Write(buffer, 0, 4);
                    networkStream.Write(cmd.MetaData, 0, cmd.MetaData.Length);
                }
	            this.networkStream.Flush();
                e.Result = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                e.Result = false;
            }
            finally
            {
                semaphore.Release();
            }
        }

        private void bwSender_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
//             if ((bool)e.Result)
//                 OnCommandSent(new EventArgs());
//             else OnCommandFailed(new EventArgs());
            ((BackgroundWorker)sender).Dispose();
            GC.Collect();
        }

        #endregion

        #region Public Methods

        public void SendCommand(Command cmd)
        {
/*
            if (cmd.Target != ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address)
                return;
*/
            BackgroundWorker bwSender = new BackgroundWorker();
            bwSender.WorkerSupportsCancellation = true;
            bwSender.DoWork += new DoWorkEventHandler(bwSender_DoWork);
            bwSender.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwSender_RunWorkerCompleted);
            bwSender.RunWorkerAsync(cmd);
        }

        #endregion
    }
}
